package dao

import (
	"context"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"strings"
)

const (
	FirstLevelMenu int32 = 1
)

type MenuDao interface {
	GetMenuDataTree(ctx context.Context) ([]Menu, error)
	GetMenuData(ctx context.Context) ([]Menu, error)
	GetLeftMenu(ctx context.Context, menuIds []int64) ([]Menu, error)
	Search(ctx context.Context, menu Menu) ([]Menu, error)
	Insert(ctx context.Context, menu Menu) error
	Update(ctx context.Context, menu Menu) error
	Del(ctx context.Context, menu Menu) error

	BuildMenuTree(menuData []Menu, parentId int64) []Menu
}

type menuDao struct {
	db *gorm.DB
}

func NewMenuDao(db *gorm.DB) MenuDao {
	return &menuDao{
		db: db,
	}
}

// GetMenuDataTree
func (m *menuDao) GetMenuDataTree(ctx context.Context) ([]Menu, error) {
	var menu []Menu
	err := m.db.WithContext(ctx).Where("menu_is_show = ? and menu_status = ?", 1, 1).Find(&menu).Error
	menuTree := m.BuildMenuTree(menu, 0)
	return menuTree, err
}

// GetMenuData 获取菜单数据
func (m *menuDao) GetMenuData(ctx context.Context) ([]Menu, error) {
	var menu []Menu
	err := m.db.WithContext(ctx).Where("menu_is_show = ? and menu_status = ?", 1, 1).Find(&menu).Error
	return menu, err
}

// GetLeftMenu 左侧菜单树
func (m *menuDao) GetLeftMenu(ctx context.Context, menuIds []int64) ([]Menu, error) {
	var menu []Menu
	query := m.db.WithContext(ctx).Where("menu_is_show = ? AND menu_status = ?", 1, 1)

	if len(menuIds) > 0 {
		query = query.Where("id IN (?)", menuIds)
	}

	err := query.Find(&menu).Error

	menuTree := m.BuildMenuTree(menu, 0)
	return menuTree, err
}

// Search 搜索菜单
func (m *menuDao) Search(ctx context.Context, menuData Menu) ([]Menu, error) {
	var menu []Menu
	query := m.db.WithContext(ctx).Where("menu_is_show = ? and menu_status = ?", 1, 1)
	if menuData.MenuTitle != "" {
		query = query.Where(" menu_title LIKE ? ", ""+menuData.MenuTitle+"%")
	}
	err := query.Find(&menu).Error
	return menu, err
}

// Update 更新菜单
func (m *menuDao) Update(ctx context.Context, menu Menu) error {
	menu.MenuUrl = m.prependSlash(menu.MenuUrl)
	if menu.JumpUrl == "" {
		menu.JumpUrl = "/"
	}
	menuLevel, err := m.getMenuLevel(ctx, menu.Pid)
	if err != nil {
		return err
	}
	if menuLevel == FirstLevelMenu {
		menu.MenuUrl = m.prependSlash(menu.MenuName)
	}
	menu.MenuLevel = menuLevel

	return m.db.WithContext(ctx).Updates(&menu).Where("id = ? ", menu.ID).Error
}

// Insert 新增菜单
func (m *menuDao) Insert(ctx context.Context, menu Menu) error {
	if !errors.Is(m.db.WithContext(ctx).Where("menu_title = ?", menu.MenuTitle).First(&menu).Error, gorm.ErrRecordNotFound) {
		return errors.New("该标题名称已存在")
	}
	menu.MenuUrl = m.prependSlash(menu.MenuUrl)
	if menu.JumpUrl == "" {
		menu.JumpUrl = "/"
	}
	menuLevel, err := m.getMenuLevel(ctx, menu.Pid)
	if err != nil {
		return err
	}
	if menuLevel == FirstLevelMenu {
		menu.MenuUrl = m.prependSlash(menu.MenuName)
	}
	menu.MenuLevel = menuLevel
	return m.db.WithContext(ctx).Create(&menu).Error
}

// Del 删除菜单
func (m *menuDao) Del(ctx context.Context, menu Menu) error {
	//TODO 删除时没有检验子级菜单的情况
	return m.db.Model(&menu).WithContext(ctx).Update("menu_status", -1).Where("id = ? ", menu.ID).Error
}

// BuildMenuTree 构建菜单树
func (m *menuDao) BuildMenuTree(menuData []Menu, parentId int64) []Menu {
	var result []Menu
	for _, menuItem := range menuData {
		if menuItem.Pid == parentId {
			// 递归构建子菜单
			menuItem.Children = m.BuildMenuTree(menuData, menuItem.ID)
			result = append(result, menuItem)
		}
	}
	return result
}

// prependSlash 检测路径开头有没有包括 / 没有拼接上一个
func (m *menuDao) prependSlash(input string) string {
	if !strings.HasPrefix(input, "/") {
		return fmt.Sprintf("/%s", strings.TrimLeft(input, "/"))
	}
	return input
}

// 获取菜单级别
func (m *menuDao) getMenuLevel(ctx context.Context, pid int64) (int32, error) {
	if pid == 0 {
		return 1, nil
	}
	var menu Menu
	err := m.db.WithContext(ctx).Where("id = ?", pid).First(&menu).Error
	if err != nil {
		return 0, err
	}
	if menu.Pid == 0 {
		return 2, nil
	}
	return 3, nil
}

type Menu struct {
	ID         int64
	MenuLevel  int32
	MenuName   string
	Pid        int64
	MenuPath   string
	MenuUrl    string
	MenuSort   int32
	MenuTitle  string
	JumpUrl    string
	CreateTime int64
	UpdateTime int64
	IsRead     int32  `gorm:"-"`
	Checked    bool   `gorm:"-"`
	Children   []Menu `gorm:"-"`
}
